//
// DotNetNuke - http://www.dotnetnuke.com
// Copyright (c) 2002-2010
// by Perpetual Motion Interactive Systems Inc. ( http://www.perpetualmotion.ca )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Services.Localization;
namespace DotNetNuke.UI.WebControls
{
	public enum EditorDisplayMode
	{
		Div,
		Table
	}
	public enum HelpDisplayMode
	{
		Never,
		EditOnly,
		Always
	}
	[ToolboxData("<{0}:FieldEditorControl runat=server></{0}:FieldEditorControl>")]
	public class FieldEditorControl : WebControl, INamingContainer
	{
		private object _DataSource;
		private string _DataField;
		private IEditorInfoAdapter _EditorInfoAdapter;
		private StandardEditorInfoAdapter _StdAdapter;
		private PropertyEditorMode _EditMode;
		private EditControl _Editor;
		private EditorDisplayMode _EditorDisplayMode = EditorDisplayMode.Div;
		private string _EditorTypeName = Null.NullString;
		private HelpDisplayMode _HelpDisplayMode = HelpDisplayMode.Always;
		private LabelMode _LabelMode = LabelMode.None;
		private Style _EditControlStyle = new Style();
		private Style _ErrorStyle = new Style();
		private Style _HelpStyle = new Style();
		private Style _LabelStyle = new Style();
		private Style _VisibilityStyle = new Style();
		private bool _IsDirty;
		private Unit _EditControlWidth;
		private Unit _LabelWidth;
		private bool _EnableClientValidation;
		private string _LocalResourceFile;
		private bool _Required;
		private string _RequiredUrl;
		private bool _ShowRequired = true;
		private bool _ShowVisibility = false;
		private List<IValidator> Validators = new List<IValidator>();
		private string _ValidationExpression = Null.NullString;
		private bool _IsValid = true;
		private bool _Validated = false;
		public event PropertyChangedEventHandler ItemAdded;
		public event PropertyChangedEventHandler ItemChanged;
		public event EditorCreatedEventHandler ItemCreated;
		public event PropertyChangedEventHandler ItemDeleted;
		protected override System.Web.UI.HtmlTextWriterTag TagKey {
			get { return HtmlTextWriterTag.Div; }
		}
		[Browsable(false)]
		public object DataSource {
			get { return _DataSource; }
			set { _DataSource= value; }
		}
		[Browsable(true), Category("Data"), DefaultValue(""), Description("Enter the name of the field that is data bound to the Control.")]
		public string DataField {
			get { return _DataField; }
			set { _DataField= value; }
		}
		public EditorDisplayMode EditorDisplayMode {
			get { return _EditorDisplayMode; }
			set { _EditorDisplayMode= value; }
		}
		public PropertyEditorMode EditMode {
			get { return _EditMode; }
			set { _EditMode= value; }
		}
		public EditControl Editor {
			get { return _Editor; }
		}
		public IEditorInfoAdapter EditorInfoAdapter {
			get {
				if (_EditorInfoAdapter == null) {
					if (_StdAdapter == null) {
						_StdAdapter = new StandardEditorInfoAdapter(DataSource, DataField);
					}
					return _StdAdapter;
				} else {
					return _EditorInfoAdapter;
				}
			}
			set { _EditorInfoAdapter= value; }
		}
		public string EditorTypeName {
			get { return _EditorTypeName; }
			set { _EditorTypeName = value; }
		}
		public bool EnableClientValidation {
			get { return _EnableClientValidation; }
			set { _EnableClientValidation= value; }
		}
		public HelpDisplayMode HelpDisplayMode {
			get { return _HelpDisplayMode; }
			set { _HelpDisplayMode= value; }
		}
		public bool IsDirty {
			get { return _IsDirty; }
		}
		public bool IsValid {
			get {
				if (!_Validated) {
					Validate();
				}
				return _IsValid;
			}
		}
		public LabelMode LabelMode {
			get { return _LabelMode; }
			set { _LabelMode = value; }
		}
		public string LocalResourceFile {
			get { return _LocalResourceFile; }
			set { _LocalResourceFile= value; }
		}
		public bool Required {
			get { return _Required; }
			set { _Required = value; }
		}
		public string RequiredUrl {
			get { return _RequiredUrl; }
			set { _RequiredUrl= value; }
		}
		public bool ShowRequired {
			get { return _ShowRequired; }
			set { _ShowRequired= value; }
		}
		public bool ShowVisibility {
			get { return _ShowVisibility; }
			set { _ShowVisibility= value; }
		}
		public string ValidationExpression {
			get { return _ValidationExpression; }
			set { _ValidationExpression= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty), TypeConverter(typeof(ExpandableObjectConverter)), Description("Set the Style for the Edit Control.")]
		public System.Web.UI.WebControls.Style EditControlStyle {
			get { return _EditControlStyle; }
		}
		[Browsable(true), Category("Appearance"), Description("Set the Width for the Edit Control.")]
		public Unit EditControlWidth {
			get { return _EditControlWidth; }
			set { _EditControlWidth= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty), TypeConverter(typeof(ExpandableObjectConverter)), Description("Set the Style for the Error Text.")]
		public System.Web.UI.WebControls.Style ErrorStyle {
			get { return _ErrorStyle; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty), TypeConverter(typeof(ExpandableObjectConverter)), Description("Set the Style for the Help Text.")]
		public System.Web.UI.WebControls.Style HelpStyle {
			get { return _HelpStyle; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty), TypeConverter(typeof(ExpandableObjectConverter)), Description("Set the Style for the Label Text")]
		public System.Web.UI.WebControls.Style LabelStyle {
			get { return _LabelStyle; }
		}
		[Browsable(true), Category("Appearance"), Description("Set the Width for the Label Control.")]
		public Unit LabelWidth {
			get { return _LabelWidth; }
			set { _LabelWidth= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty), TypeConverter(typeof(ExpandableObjectConverter)), Description("Set the Style for the Visibility Control")]
		public System.Web.UI.WebControls.Style VisibilityStyle {
			get { return _VisibilityStyle; }
		}
		private void BuildDiv(EditorInfo editInfo)
		{
			HtmlGenericControl divLabel = null;
			if (editInfo.LabelMode != LabelMode.None) {
				divLabel = new HtmlGenericControl("div");
				string style = "float: " + editInfo.LabelMode.ToString().ToLower();
				if (editInfo.LabelMode == LabelMode.Left || editInfo.LabelMode == LabelMode.Right)
				{
					style += "; width: " + LabelWidth.ToString();
				}
				divLabel.Attributes.Add("style", style);
				divLabel.Controls.Add(BuildLabel(editInfo));
			}
			HtmlGenericControl divEdit = new HtmlGenericControl("div");
			string side = GetOppositeSide(editInfo.LabelMode);
			if (!String.IsNullOrEmpty(side)) {
				string style = "float: " + side;
				style += "; width: " + EditControlWidth.ToString();
				divEdit.Attributes.Add("style", style);
			}
			EditControl propEditor = BuildEditor(editInfo);
			VisibilityControl visibility = BuildVisibility(editInfo);
			if (visibility != null) {
				visibility.Attributes.Add("style", "float: right;");
				divEdit.Controls.Add(visibility);
			}

		    LiteralControl lb = new LiteralControl();
		    lb.Text = "<p>&nbsp;</p>";
            //if injecting a texteditcontrol add a paragraph for spacing (clumsy hack to be removed when UX overhaul occurs)
            if(propEditor is DNNRichTextEditControl)
            {
                divEdit.Controls.Add(lb);
            }

		    divEdit.Controls.Add(propEditor);
			System.Web.UI.WebControls.Image requiredIcon = BuildRequiredIcon(editInfo);
			if (requiredIcon != null) {
				divEdit.Controls.Add(requiredIcon);
			}
			if (editInfo.LabelMode == LabelMode.Left || editInfo.LabelMode == LabelMode.Top)
			{
				Controls.Add(divLabel);
				Controls.Add(divEdit);
			} else {
				Controls.Add(divEdit);
				if ((divLabel != null)) {
					Controls.Add(divLabel);
				}
			}
			BuildValidators(editInfo, propEditor.ID);
			if (Validators.Count > 0) {
				foreach (BaseValidator validator in Validators) {
					validator.Width = this.Width;
					Controls.Add(validator);
				}
			}
		}
		private EditControl BuildEditor(EditorInfo editInfo)
		{
			EditControl propEditor = EditControlFactory.CreateEditControl(editInfo);
			propEditor.ControlStyle.CopyFrom(EditControlStyle);
			propEditor.LocalResourceFile = LocalResourceFile;
			if (editInfo.ControlStyle != null) {
				propEditor.ControlStyle.CopyFrom(editInfo.ControlStyle);
			}
			propEditor.ItemAdded += this.CollectionItemAdded;
			propEditor.ItemDeleted += this.CollectionItemDeleted;
			propEditor.ValueChanged += this.ValueChanged;
			if (propEditor is DNNListEditControl) {
				DNNListEditControl listEditor = (DNNListEditControl)propEditor;
				listEditor.ItemChanged += this.ListItemChanged;
			}
			_Editor = propEditor;
			return propEditor;
		}
		private PropertyLabelControl BuildLabel(EditorInfo editInfo)
		{
			PropertyLabelControl propLabel = new PropertyLabelControl();
			propLabel.ID = editInfo.Name + "_Label";
			propLabel.HelpStyle.CopyFrom(HelpStyle);
			propLabel.LabelStyle.CopyFrom(LabelStyle);
			string strValue = editInfo.Value as string;
			switch (HelpDisplayMode) {
				case HelpDisplayMode.Always:
					propLabel.ShowHelp = true;
					break;
				case HelpDisplayMode.EditOnly:
					if (editInfo.EditMode == PropertyEditorMode.Edit || (editInfo.Required && string.IsNullOrEmpty(strValue)))
					{
						propLabel.ShowHelp = true;
					} else {
						propLabel.ShowHelp = false;
					}
					break;
				case HelpDisplayMode.Never:
					propLabel.ShowHelp = false;
					break;
			}
			propLabel.Caption = editInfo.Name;
			propLabel.HelpText = editInfo.Name;
			propLabel.ResourceKey = editInfo.ResourceKey;
			if (editInfo.LabelMode == LabelMode.Left || editInfo.LabelMode == LabelMode.Right)
			{
				propLabel.Width = LabelWidth;
			}
			return propLabel;
		}
		private System.Web.UI.WebControls.Image BuildRequiredIcon(EditorInfo editInfo)
		{
			System.Web.UI.WebControls.Image img = null;
			string strValue = editInfo.Value as string;
			if (ShowRequired && editInfo.Required && (editInfo.EditMode == PropertyEditorMode.Edit || (editInfo.Required && string.IsNullOrEmpty(strValue))))
			{
				img = new System.Web.UI.WebControls.Image();
				if (String.IsNullOrEmpty(RequiredUrl) || RequiredUrl == Null.NullString)
				{
					img.ImageUrl = "~/images/required.gif";
				} else {
					img.ImageUrl = RequiredUrl;
				}
				img.Attributes.Add("resourcekey", editInfo.ResourceKey + ".Required");
			}
			return img;
		}
		private void BuildTable(EditorInfo editInfo)
		{
			Table tbl = new Table();
			TableCell labelCell = new TableCell();
			TableCell editorCell = new TableCell();
			labelCell.VerticalAlign = VerticalAlign.Top;
			labelCell.Controls.Add(BuildLabel(editInfo));
			if (editInfo.LabelMode == LabelMode.Left || editInfo.LabelMode == LabelMode.Right)
			{
				labelCell.Width = LabelWidth;
			}
			editorCell.VerticalAlign = VerticalAlign.Top;
			EditControl propEditor = BuildEditor(editInfo);
			System.Web.UI.WebControls.Image requiredIcon = BuildRequiredIcon(editInfo);
			editorCell.Controls.Add(propEditor);
			if (requiredIcon != null) {
				editorCell.Controls.Add(requiredIcon);
			}
			if (editInfo.LabelMode == LabelMode.Left || editInfo.LabelMode == LabelMode.Right)
			{
				editorCell.Width = EditControlWidth;
			}
			VisibilityControl visibility = BuildVisibility(editInfo);
			if (visibility != null) {
				editorCell.Controls.Add(new LiteralControl("&nbsp;&nbsp;"));
				editorCell.Controls.Add(visibility);
			}
			TableRow editorRow = new TableRow();
			TableRow labelRow = new TableRow();
			if (editInfo.LabelMode == LabelMode.Bottom || editInfo.LabelMode == LabelMode.Top || editInfo.LabelMode == LabelMode.None)
			{
				editorCell.ColumnSpan = 2;
				editorRow.Cells.Add(editorCell);
				if (editInfo.LabelMode == LabelMode.Bottom || editInfo.LabelMode == LabelMode.Top)
				{
					labelCell.ColumnSpan = 2;
					labelRow.Cells.Add(labelCell);
				}
				if (editInfo.LabelMode == LabelMode.Top) {
					tbl.Rows.Add(labelRow);
				}
				tbl.Rows.Add(editorRow);
				if (editInfo.LabelMode == LabelMode.Bottom) {
					tbl.Rows.Add(labelRow);
				}
			} else if (editInfo.LabelMode == LabelMode.Left) {
				editorRow.Cells.Add(labelCell);
				editorRow.Cells.Add(editorCell);
				tbl.Rows.Add(editorRow);
			} else if (editInfo.LabelMode == LabelMode.Right) {
				editorRow.Cells.Add(editorCell);
				editorRow.Cells.Add(labelCell);
				tbl.Rows.Add(editorRow);
			}
			BuildValidators(editInfo, propEditor.ID);
			TableRow validatorsRow = new TableRow();
			TableCell validatorsCell = new TableCell();
			validatorsCell.ColumnSpan = 2;
			foreach (BaseValidator validator in Validators) {
				validatorsCell.Controls.Add(validator);
			}
			validatorsRow.Cells.Add(validatorsCell);
			tbl.Rows.Add(validatorsRow);
			Controls.Add(tbl);
		}
		private void BuildValidators(EditorInfo editInfo, string targetId)
		{
			Validators.Clear();
			if (editInfo.Required) {
				RequiredFieldValidator reqValidator = new RequiredFieldValidator();
				reqValidator.ID = editInfo.Name + "_Req";
				reqValidator.ControlToValidate = targetId;
				reqValidator.Display = ValidatorDisplay.Dynamic;
				reqValidator.ControlStyle.CopyFrom(ErrorStyle);
				reqValidator.EnableClientScript = EnableClientValidation;
				reqValidator.Attributes.Add("resourcekey", editInfo.ResourceKey + ".Required");
				reqValidator.ErrorMessage = editInfo.Name + " is Required";
				Validators.Add(reqValidator);
			}
			if (!String.IsNullOrEmpty(editInfo.ValidationExpression)) {
				RegularExpressionValidator regExValidator = new RegularExpressionValidator();
				regExValidator.ID = editInfo.Name + "_RegEx";
				regExValidator.ControlToValidate = targetId;
				regExValidator.ValidationExpression = editInfo.ValidationExpression;
				regExValidator.Display = ValidatorDisplay.Dynamic;
				regExValidator.ControlStyle.CopyFrom(ErrorStyle);
				regExValidator.EnableClientScript = EnableClientValidation;
				regExValidator.Attributes.Add("resourcekey", editInfo.ResourceKey + ".Validation");
				regExValidator.ErrorMessage = editInfo.Name + " is Invalid";
				Validators.Add(regExValidator);
			}
		}
		private VisibilityControl BuildVisibility(EditorInfo editInfo)
		{
			VisibilityControl visControl = null;
			if (ShowVisibility) {
				visControl = new VisibilityControl();
				visControl.ID = this.ID + "_vis";
				visControl.Caption = Localization.GetString("Visibility");
				visControl.Name = editInfo.Name;
				visControl.Value = editInfo.Visibility;
				visControl.ControlStyle.CopyFrom(VisibilityStyle);
				visControl.VisibilityChanged += this.VisibilityChanged;
			}
			return visControl;
		}
		private string GetOppositeSide(LabelMode labelMode)
		{
			switch (labelMode) {
				case LabelMode.Bottom:
					return "top";
				case LabelMode.Left:
					return "right";
				case LabelMode.Right:
					return "left";
				case LabelMode.Top:
					return "bottom";
				default:
					return string.Empty;
			}
		}
		protected virtual void CreateEditor()
		{
			EditorInfo editInfo = EditorInfoAdapter.CreateEditControl();
			if (editInfo.EditMode == PropertyEditorMode.Edit) {
				editInfo.EditMode = EditMode;
			}
			if (!string.IsNullOrEmpty(EditorTypeName)) {
				editInfo.Editor = EditorTypeName;
			}
			if (LabelMode != UI.WebControls.LabelMode.Left) {
				editInfo.LabelMode = LabelMode;
			}
			if (Required) {
				editInfo.Required = Required;
			}
			if (!string.IsNullOrEmpty(ValidationExpression)) {
				editInfo.ValidationExpression = ValidationExpression;
			}
			OnItemCreated(new PropertyEditorItemEventArgs(editInfo));
			this.Visible = editInfo.Visible;
			if (EditorDisplayMode == EditorDisplayMode.Div) {
				BuildDiv(editInfo);
			} else {
				BuildTable(editInfo);
			}
		}
		protected virtual void CollectionItemAdded(object sender, PropertyEditorEventArgs e)
		{
			OnItemAdded(e);
		}
		protected virtual void CollectionItemDeleted(object sender, PropertyEditorEventArgs e)
		{
			OnItemDeleted(e);
		}
		protected virtual void OnItemAdded(PropertyEditorEventArgs e)
		{
			if (ItemAdded != null) {
				ItemAdded(this, e);
			}
		}
		protected virtual void OnItemCreated(PropertyEditorItemEventArgs e)
		{
			if (ItemCreated != null) {
				ItemCreated(this, e);
			}
		}
		protected virtual void OnItemDeleted(PropertyEditorEventArgs e)
		{
			if (ItemDeleted != null) {
				ItemDeleted(this, e);
			}
		}
		protected virtual void ValueChanged(object sender, PropertyEditorEventArgs e)
		{
			_IsDirty = EditorInfoAdapter.UpdateValue(e);
		}
		protected virtual void VisibilityChanged(object sender, PropertyEditorEventArgs e)
		{
			_IsDirty = EditorInfoAdapter.UpdateVisibility(e);
		}
		public override void DataBind()
		{
			base.OnDataBinding(EventArgs.Empty);
			Controls.Clear();
			ClearChildViewState();
			TrackViewState();
			CreateEditor();
			ChildControlsCreated = true;
		}
		public virtual void Validate()
		{
			_IsValid = Editor.IsValid;
			if (_IsValid) {
				IEnumerator valEnumerator = Validators.GetEnumerator();
				while (valEnumerator.MoveNext()) {
					IValidator validator = (IValidator)valEnumerator.Current;
					validator.Validate();
					if (!validator.IsValid) {
						_IsValid = false;
						break;
					}
				}
				_Validated = true;
			}
		}
		protected virtual void ListItemChanged(object sender, PropertyEditorEventArgs e)
		{
			if (ItemChanged != null) {
				ItemChanged(this, e);
			}
		}
	}
}
